<?php

namespace nimo\domains\master\batch\accounting;

use DB;
use DateTime;
use nimo\common\csv\Importer;
use nimo\common\csv\Exporter;
use nimo\common\csv\SimpleInterceptor;
use nimo\common\csv\DatabaseInterceptor;
use nimo\common\csv\CsvConverter;
use nimo\common\design\PComposite;
use nimo\common\design\IVisitor;

class PrimitiveAccountBatch implements IVisitor {
    protected $file;
    protected $configs;
    protected $converter;
    protected $roots;
    protected $batch_size;
    protected $domainid;
    public function __construct($file, $configs, CsvConverter $converter) {
        $this->file = $file;
        $this->configs = $configs;
        $this->converter = $converter;
        $this->domainid = isset($configs['domainid']) ? $configs['domainid'] : 0;
        $this->batch_size = isset($configs['batch_size']) ? $configs['batch_size'] : null;
    }
    public function roots() {
        return $this->roots;
    }
    public function export() {
        // database
        $query = DB::table('nd_master_account')->where('domainid', '=', $this->domainid)->orderBy('id', 'asc');
        $di = new DatabaseInterceptor('nd_master_account', $this->converter);
        $di->set_query($query->toSql(), array($this->domainid));
        $ci = new Exporter($this->file, $this->configs, $di);
        $ci->execute();
    }
    public function import() {
        // read file
        $si = new SimpleInterceptor();
        $ci = new Importer($this->file, $this->configs, $si);
        $ci->execute();

        $this->nestid = 0;
        $this->nestbound = 0;
        $this->indent = - 1;
        $this->datas = array();

        // create composite model
        $this->roots = PComposite::makeTreeByDepth($si->getDatas(), $this->converter);

        // set node model paramters
        $this->step = 1;
        foreach ( $this->roots as $root ) {
            $root->visit($this);
        }

        // write to database
        $this->step = 2;
        $this->indent = - 1;
        DB::transaction(function () {
            $this->batchtime = new DateTime();
            DB::table('nd_master_account')->delete();
            foreach ( $this->roots as $root ) {
                $fields = $root->fields();
                if (in_array($fields[$this->converter->index('要素名')], array('StatementsOfComprehensiveIncomeAbstract','StatementsOfChangesInNetAssetsAbstract'))) {
                    continue;
                }
                $root->visit($this);
            }
            if (count($this->datas)) {
                DB::table('nd_master_account')->insert($this->datas);
            }
        });
    }
    private $step = 0;
    private $nestid = 0;
    private $nestbound = 0;
    private $indent = 0;
    private $datas = array();
    private $batchtime = null;
    public function entering($visitor) {
        $this->indent ++;
        if ($this->step == 1) {
            // STEP1
            // PComposite
            $this->nestid ++;
            $this->nestbound ++;
            // tree
            $visitor->set_field('id', $this->nestid);
            if ($visitor->parent()) {
                $fields = $visitor->parent()->fields();
                $parent = $fields['id'];
            } else {
                $parent = 0;
            }
            $visitor->set_field('node_parent', $parent);
            $visitor->set_field('node_left', $this->nestbound);
            $visitor->set_field('node_depth', $this->indent);
        }
    }
    public function visit($visitor) {
        if ($this->step == 2) {
            // STEP2
            // PComposite
            $fields = $visitor->fields();
            $data = $this->converter->convert($fields);
            foreach ( array('id','node_parent','node_left','node_right','node_depth') as $key ) {
                $data[$key] = $fields[$key];
            }
            // code
            $data['domainid'] = $this->domainid;
            $data['code'] = $this->calc_code($visitor);
            $data['created_at'] = $this->batchtime;
            $data['updated_at'] = $this->batchtime;

            if ($this->batch_size === null) {
                $configs = app('config');
                $max_bind_number = (isset($configs['database.max_bind_number'])) ? $configs['database.max_bind_number'] : 999;
                $this->batch_size = intval($max_bind_number / count($data)) - 1;
            }
            $this->datas[] = $data;
            // $this->debug($data);
            if (count($this->datas) >= $this->batch_size) {
                DB::table('nd_master_account')->insert($this->datas);
                $this->datas = array();
            }
        }
    }
    public function leaving($visitor) {
        $this->indent --;
        if ($this->step == 1) {
            // STEP1
            $this->nestbound ++;
            // PComposite
            $visitor->set_field('node_right', $this->nestbound);
        }
    }
    private $code_abstract = 0;
    private $code = 0;
    public function is_abstract($visitor) {
        $ind = $this->converter->index('abstract');
        $fields = $visitor->parent()->fields();
        $abstract = $fields[$ind];
        return ($abstract == 'true' || $abstract == '1');
    }
    public function calc_code($visitor) {
        $code = 0;
        $ind = $this->converter->index('要素名');
        $fields = $visitor->parent()->fields();
        $nameid = $fields[$ind];

        if ($this->is_abstract($visitor)) {
            if ($visitor->is_root()) {
                $this->code = intval($this->code / 10000) * 10000 + 10000;
                $this->code_abstract = intval($this->code_abstract / 10000000) * 10000000 + 10000000;
            } else {
                if ($this->converter->is_group($visitor->fields())) {
                    if ($this->code % 1000 > 0) {
                        $this->code = intval($this->code / 1000) * 1000 + 1000;
                    }
                    $this->code_abstract = intval($this->code_abstract / 100) * 100 + 100;
                }
            }
            $code = $this->code_abstract;
            $this->code_abstract = $this->code_abstract + 10;
        } else {
            $code = $this->code;
            $this->code = $this->code + 10;
        }
        return $code;
    }
    public function debug($data) {
        if ($data['node_depth'] == 0)
            echo "----------------------------\n";
        $format = '%' . (($data['node_depth'] == 0) ? '' : (2 * $data['node_depth'])) . 's%-' . (100 - 2 * $data['node_depth']) . "s%s";
        echo sprintf($format, '', $data['nameid'], implode(' ', array($data['node_depth'],$data['abstract'],$data['type'],$data['code'],$data['name'],"\n")));
    }
}
